﻿using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Text;
using NetMicro.Core.Extensions;
using NetMicro.Core.Helper;
using NetMicro.Core.Ioc.ServiceLocation;
using NetMicro.HttpClient.AspectExtend;
using NetMicro.HttpClient.Attributes;
using NetMicro.HttpClient.Attributes.HttpMethods;
using NetMicro.HttpClient.Attributes.Parameters;
using NetMicro.HttpClient.Enums;
using NetMicro.HttpClient.Internal;
using NetMicro.Nacos.Abstractions;
using NetMicro.Validations.Extensions;

namespace NetMicro.HttpClient.Extensions
{
    /// <summary>
    /// 创建HttpClientPart扩展
    /// </summary>
    public static class HttpClientPartExtensions
    {
        /// <summary>
        /// 构建 HttpClient serviceBus 请求部件
        /// </summary>
        /// <param name="method"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static HttpClientPart BuildServiceBusHttpClientPart(this MethodInfo method, object[] args)
        {

            // 判断方法是否是远程代理请求方法
            if (!method.IsDefined(typeof(HttpMethodBaseAttribute), true)) throw new InvalidOperationException($"{method.Name} is not a valid request proxy method.");

            // 解析方法参数及参数值
            IEnumerable<MethodParameterInfo> parameters = method.GetParameters().Select((u, i) => new MethodParameterInfo
            {
                Parameter = u,
                Name = u.Name,
                Value = args[i]
            });

            // 获取请求配置
            HttpMethodBaseAttribute httpMethodBase = method.GetCustomAttribute<HttpMethodBaseAttribute>(true);

            // 获取方法所在类型
            Type declaringType = method.DeclaringType;

            //获取服务名称

            ServiceNameAttribute service = method.IsDefined(typeof(ServiceNameAttribute), true)
          ? method.GetCustomAttribute<ServiceNameAttribute>(true)
          : (
              declaringType.IsDefined(typeof(ServiceNameAttribute), true)
              ? declaringType.GetCustomAttribute<ServiceNameAttribute>(true)
              : default
          );
            string url = GetServerUrlAsync(service?.Name);


            // 创建请求配置对象
            HttpClientPart httpClientPart = new HttpClientPart();
            httpClientPart.SetRequestUrl($"{url?.TrimEnd('/')}/{httpMethodBase.RequestUrl.TrimStart('/')}")
                      .SetHttpMethod(httpMethodBase.Method);

            // 设置请求客户端
            SetClient(method, httpClientPart, declaringType);

            //设置polly策略
            SetPolly(method, httpClientPart, declaringType);

            // 设置请求报文头
            SetHeaders(method, parameters, httpClientPart, declaringType);

            // 设置 Url 地址参数
            SetQueries(parameters, httpClientPart);

            // 设置 Body 信息
            SetBody(parameters, httpClientPart);

            // 设置验证
            SetValidation(parameters);


            // 配置全局拦截
            CallGlobalInterceptors(httpClientPart, declaringType);
            // 设置请求拦截
            SetInterceptors(parameters, httpClientPart);

            return httpClientPart;
        }


        /// <summary>
        /// 获取服务地址
        /// </summary>
        /// <returns></returns>
        private static string GetServerUrlAsync(string serviceName)
        {
            string url = ServiceLocator.Current.Create<IServiceClient>().GetAsync(serviceName).Result;
            if (url == null)
            {
                ServiceLocator.Current.Create<ILogger<HttpServiceBusProxy>>()?.LogError($"{serviceName}未注册，调用nacos找不到服务");
                throw new ArgumentNullException(serviceName, "未注册，调用nacos找不到服务");
            }
            return url;
        }
        /// <summary>
        /// 构建 HttpClient 请求部件
        /// </summary>
        /// <param name="method"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static HttpClientPart BuildHttpClientPart(this MethodInfo method, object[] args)
        {
            // 判断方法是否是远程代理请求方法
            if (!method.IsDefined(typeof(HttpMethodBaseAttribute), true)) throw new InvalidOperationException($"{method.Name} is not a valid request proxy method.");

            // 解析方法参数及参数值
            IEnumerable<MethodParameterInfo> parameters = method.GetParameters().Select((u, i) => new MethodParameterInfo
            {
                Parameter = u,
                Name = u.Name,
                Value = args[i]
            });

            // 获取请求配置
            HttpMethodBaseAttribute httpMethodBase = method.GetCustomAttribute<HttpMethodBaseAttribute>(true);

            // 创建请求配置对象
            HttpClientPart httpClientPart = new HttpClientPart();
            httpClientPart.SetRequestUrl(httpMethodBase.RequestUrl)
                          .SetHttpMethod(httpMethodBase.Method)
                          .SetTemplates(parameters.ToDictionary(u => u.Name, u => u.Value));

            // 获取方法所在类型
            Type declaringType = method.DeclaringType;

            // 设置请求客户端
            SetClient(method, httpClientPart, declaringType);
            //设置polly策略
            SetPolly(method, httpClientPart, declaringType);
            // 设置请求报文头
            SetHeaders(method, parameters, httpClientPart, declaringType);

            // 设置 Url 地址参数
            SetQueries(parameters, httpClientPart);

            // 设置 Body 信息
            SetBody(parameters, httpClientPart);
            // 设置验证
            SetValidation(parameters);


            // 配置全局拦截
            CallGlobalInterceptors(httpClientPart, declaringType);
            // 设置请求拦截
            SetInterceptors(parameters, httpClientPart);

            return httpClientPart;
        }

        /// <summary>
        /// 设置客户端信息
        /// </summary>
        /// <param name="method"></param>
        /// <param name="httpClientPart"></param>
        /// <param name="declaringType"></param>
        private static void SetClient(MethodInfo method, HttpClientPart httpClientPart, Type declaringType)
        {
            // 设置 Client 名称，判断方法是否定义，如果没有再查找声明类
            HttpClientAttribute clientAttribute = method.IsDefined(typeof(HttpClientAttribute), true)
                ? method.GetCustomAttribute<HttpClientAttribute>(true)
                : (
                    declaringType.IsDefined(typeof(HttpClientAttribute), true)
                    ? declaringType.GetCustomAttribute<HttpClientAttribute>(true)
                    : default
                );
            if (clientAttribute != null) httpClientPart.SetClient(clientAttribute.Name);
        }

        /// <summary>
        /// 设置 Url 地址参数
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="httpClientPart"></param>
        private static void SetQueries(IEnumerable<MethodParameterInfo> parameters, HttpClientPart httpClientPart)
        {
            // 配置 Url 地址参数
            IEnumerable<MethodParameterInfo> queryParameters = parameters.Where(u => u.Parameter.IsDefined(typeof(QueryStringAttribute), true));
            Dictionary<string, object> parameterQueries = new Dictionary<string, object>();
            foreach (MethodParameterInfo item in queryParameters)
            {
                QueryStringAttribute queryStringAttribute = item.Parameter.GetCustomAttribute<QueryStringAttribute>();
                if (item.Value != null) parameterQueries.Add(queryStringAttribute.Alias ?? item.Name, item.Value);
            }
            httpClientPart.SetQueries(parameterQueries);

            IEnumerable<MethodParameterInfo> queryModelParameters = parameters.Where(u => u.Parameter.IsDefined(typeof(QueryModelAttribute), true));
            foreach (MethodParameterInfo item in queryModelParameters)
            {
                httpClientPart.SetQueries(item.Value);
            }
        }

        /// <summary>
        /// 设置 Body 参数
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="httpClientPart"></param>
        private static void SetBody(IEnumerable<MethodParameterInfo> parameters, HttpClientPart httpClientPart)
        {
            // 配置 Body 参数，只取第一个
            MethodParameterInfo bodyParameter = parameters.FirstOrDefault(u => u.Parameter.IsDefined(typeof(BodyAttribute), true));
            if (bodyParameter != null)
            {
                BodyAttribute bodyAttribute = bodyParameter.Parameter.GetCustomAttribute<BodyAttribute>(true);
                httpClientPart.SetBody(bodyParameter.Value, bodyAttribute.ContentType, Encoding.GetEncoding(bodyAttribute.Encoding))
                              .SetValidationState(true);   // 开启验证
            }

            // 查找所有贴了 [BodyBytes] 特性的参数
            IEnumerable<MethodParameterInfo> bodyBytesParameters = parameters.Where(u => u.Parameter.IsDefined(typeof(BodyBytesAttribute), true));
            if (bodyBytesParameters != null)
            {
                List<(string Name, byte[] Bytes, string FileName)> bodyBytes = new List<(string Name, byte[] Bytes, string FileName)>();
                foreach (MethodParameterInfo item in bodyBytesParameters)
                {
                    BodyBytesAttribute bodyBytesAttribute = item.Parameter.GetCustomAttribute<BodyBytesAttribute>();
                    if (item.Value != null && item.Value.GetType() == typeof(byte[])) bodyBytes.Add((bodyBytesAttribute.Alias ?? item.Name, (byte[])item.Value, bodyBytesAttribute.FileName));
                }

                httpClientPart.SetBodyBytes(bodyBytes);
            }
        }



        /// <summary>
        /// 设置验证
        /// </summary>
        /// <param name="parameters"></param>
        private static void SetValidation(IEnumerable<MethodParameterInfo> parameters)
        {
            // 验证参数，查询所有配置验证特性的参数，排除 Body 验证
            IEnumerable<MethodParameterInfo> validateParameters = parameters.Where(u => u.Parameter.IsDefined(typeof(ValidationAttribute), true) && !u.Parameter.IsDefined(typeof(BodyAttribute), true));
            foreach (MethodParameterInfo item in validateParameters)
            {
                // 处理空值
                bool isRequired = item.Parameter.IsDefined(typeof(RequiredAttribute), true);
                if (isRequired && item.Value == null) throw new InvalidOperationException($"{item.Name} can not be null.");

                // 判断是否是基元类型
                if (item.Parameter.ParameterType.IsRichPrimitive())
                {
                    IEnumerable<ValidationAttribute> validationAttributes = item.Parameter.GetCustomAttributes<ValidationAttribute>(true);
                    item.Value?.Validate(validationAttributes.ToArray());
                }
                else item.Value?.Validate();
            }
        }

        /// <summary>
        /// 调用全局拦截
        /// </summary>
        /// <param name="httpClientPart"></param>
        /// <param name="declareType"></param>
        private static void CallGlobalInterceptors(HttpClientPart httpClientPart, Type declareType)
        {
            // 获取所有静态方法且贴有 [Interceptor] 特性
            IEnumerable<MethodInfo> interceptorMethods = declareType.GetMethods()
                                                                  .Where(u => u.IsDefined(typeof(InterceptorAttribute), true));

            foreach (MethodInfo method in interceptorMethods)
            {
                // 获取拦截器类型
                InterceptorAttribute interceptor = method.GetCustomAttributes<InterceptorAttribute>().First();
                switch (interceptor.Type)
                {
                    // 加载请求拦截
                    case InterceptorTypes.Request:
                        Action<HttpRequestMessage> onRequesting = (Action<HttpRequestMessage>)Delegate.CreateDelegate(typeof(Action<HttpRequestMessage>), method);
                        httpClientPart.OnRequesting(onRequesting);
                        break;
                    // 加载响应拦截
                    case InterceptorTypes.Response:
                        Action<HttpResponseMessage> onResponsing = (Action<HttpResponseMessage>)Delegate.CreateDelegate(typeof(Action<HttpResponseMessage>), method);
                        httpClientPart.OnResponsing(onResponsing);
                        break;
                    // 加载 Client 配置拦截
                    case InterceptorTypes.HttpClient:
                        Action<System.Net.Http.HttpClient> onClientCreating = (Action<System.Net.Http.HttpClient>)Delegate.CreateDelegate(typeof(Action<System.Net.Http.HttpClient>), method);
                        httpClientPart.OnClientCreating(onClientCreating);
                        break;
                    // 加载异常拦截
                    case InterceptorTypes.Exception:
                        Action<HttpResponseMessage, string> onException = (Action<HttpResponseMessage, string>)Delegate.CreateDelegate(typeof(Action<HttpResponseMessage, string>), method);
                        httpClientPart.OnException(onException);
                        break;

                    default: break;
                }
            }
        }

        /// <summary>
        /// 设置请求拦截
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="httpClientPart"></param>
        private static void SetInterceptors(IEnumerable<MethodParameterInfo> parameters, HttpClientPart httpClientPart)
        {
            // 添加方法拦截器
            IEnumerable<MethodParameterInfo> Interceptors = parameters.Where(u => u.Parameter.IsDefined(typeof(InterceptorAttribute), true));
            foreach (MethodParameterInfo item in Interceptors)
            {
                // 获取拦截器类型
                InterceptorAttribute interceptor = item.Parameter.GetCustomAttribute<InterceptorAttribute>();
                switch (interceptor.Type)
                {
                    // 加载请求拦截
                    case InterceptorTypes.Request:
                        if (item.Value is Action<HttpRequestMessage> onRequesting)
                        {
                            httpClientPart.OnRequesting(onRequesting);
                        }
                        break;
                    // 加载响应拦截
                    case InterceptorTypes.Response:
                        if (item.Value is Action<HttpResponseMessage> onResponsing)
                        {
                            httpClientPart.OnResponsing(onResponsing);
                        }
                        break;
                    // 加载 Client 配置拦截
                    case InterceptorTypes.HttpClient:
                        if (item.Value is Action<System.Net.Http.HttpClient> onClientCreating)
                        {
                            httpClientPart.OnClientCreating(onClientCreating);
                        }
                        break;
                    // 加载异常拦截
                    case InterceptorTypes.Exception:
                        if (item.Value is Action<HttpResponseMessage, string> onException)
                        {
                            httpClientPart.OnException(onException);
                        }
                        break;

                    default: break;
                }
            }
        }

        /// <summary>
        /// 设置请求报文头
        /// </summary>
        /// <param name="method"></param>
        /// <param name="parameters"></param>
        /// <param name="httpClientPart"></param>
        /// <param name="declaringType"></param>
        private static void SetHeaders(MethodInfo method, IEnumerable<MethodParameterInfo> parameters, HttpClientPart httpClientPart, Type declaringType)
        {
            // 获取声明类请求报文头
            Dictionary<string, object> declaringTypeHeaders = (declaringType.IsDefined(typeof(HeadersAttribute), true)
                ? declaringType.GetCustomAttributes<HeadersAttribute>(true)
                : Array.Empty<HeadersAttribute>()).ToDictionary(u => u.Key, u => u.Value);

            // 获取方法请求报文头
            Dictionary<string, object> methodHeaders = (method.IsDefined(typeof(HeadersAttribute), true)
                ? method.GetCustomAttributes<HeadersAttribute>(true)
                : Array.Empty<HeadersAttribute>()).ToDictionary(u => u.Key, u => u.Value);

            // 获取参数请求报文头
            IEnumerable<MethodParameterInfo> headerParameters = parameters.Where(u => u.Parameter.IsDefined(typeof(HeadersAttribute), true));
            Dictionary<string, object> parameterHeaders = new Dictionary<string, object>();
            foreach (MethodParameterInfo item in headerParameters)
            {
                HeadersAttribute headersAttribute = item.Parameter.GetCustomAttribute<HeadersAttribute>(true);
                if (item.Value != null) parameterHeaders.Add(headersAttribute.Key ?? item.Name, item.Value);
            }

            // 合并所有请求报文头
            Dictionary<string, object> headers = declaringTypeHeaders.AddOrUpdate(methodHeaders).AddOrUpdate(parameterHeaders);

            bool token = headers.TryGetValue("Authorization", out _);
            if (!token)
            {
                var token_result = WebHelper.Request?.Headers["Authorization"].SafeString();
                if (!string.IsNullOrWhiteSpace(token_result))
                    headers.TryAdd("Authorization", WebHelper.Request?.Headers["Authorization"].SafeString());
            }
            httpClientPart.SetHeaders(headers);
        }

        /// <summary>
        /// 设置客户端信息
        /// </summary>
        /// <param name="method"></param>
        /// <param name="httpClientPart"></param>
        /// <param name="declaringType"></param>
        private static void SetPolly(MethodInfo method, HttpClientPart httpClientPart, Type declaringType)
        {
            // 设置 Client 名称，判断方法是否定义，如果没有再查找声明类
            StrategyAttribute strategyAttribute = method.IsDefined(typeof(StrategyAttribute), true)
                ? method.GetCustomAttribute<StrategyAttribute>(true)
                : (
                    declaringType.IsDefined(typeof(StrategyAttribute), true)
                    ? declaringType.GetCustomAttribute<StrategyAttribute>(true)
                    : default
                );
            if (strategyAttribute != null)
            {
                httpClientPart.SetMaxRetryTimes(strategyAttribute.MaxRetryTimes);
                httpClientPart.SetRetryIntervalMilliseconds(strategyAttribute.RetryIntervalMilliseconds);
                httpClientPart.SetIsEnableCircuitBreaker(strategyAttribute.IsEnableCircuitBreaker);
                httpClientPart.SetExceptionsAllowedBeforeBreaking(strategyAttribute.ExceptionsAllowedBeforeBreaking);
                httpClientPart.SetMillisecondsOfBreak(strategyAttribute.MillisecondsOfBreak);
                httpClientPart.SetIsEnablePolly();
            }
        }

    }
}
